package basics;

import java.util.Arrays;
import java.util.List;

public abstract class Vector implements VectorMatrix {

	public Vector timesCopy(double d) {
		return entrywiseProduct(this, d);
	}

	// public Vector timesCopy(Vector x) {
	// return entrywiseProduct(this, x);
	// }

	// public abstract double get(int index);

	// public abstract int length();

	// public abstract void set(int index, double v);

	protected abstract Vector newInstance(int n);

	public Vector sum(Vector a, Vector b) {
		Vector v = newInstance(a.length());
		sum(v, a, b);
		return v;
	}

	protected static void sum(VectorMatrix v, VectorMatrix a, VectorMatrix b) {
		for (int i = 0; i < a.length(); i++) {
			v.set(a.get(i) + b.get(i), i);
		}
	}

	public Vector add(Vector b) {
		sum(this, this, b);
		return this;
	}

	public void add(double d) {
		for (int i = 0; i < this.length(); i++) {
			this.set(this.get(i) + d, i);
		}
	}

	public Vector entrywiseProduct(Vector a, Vector b) {
		Vector v = newInstance(a.length());
		entrywiseProduct(a, b, v);

		return v;
	}

	protected static void entrywiseProduct(Vector v, Vector a, Vector b) {
		for (int i = 0; i < a.length(); i++) {
			v.set(a.get(i) * b.get(i), i);
		}
	}

	public Vector entrywiseProduct(Vector b) {
		entrywiseProduct(this, this, b);
		return this;
	}

	public Vector entrywiseProduct(Vector a, double b) {
		Vector v = newInstance(a.length());
		entrywiseProduct(v, a, b);
		return v;
	}

	protected static void entrywiseProduct(Vector v, Vector a, double b) {
		for (int i = 0; i < a.length(); i++) {
			v.set(a.get(i) * b, i);
		}
	}

	public Vector entrywiseProduct(double a) {
		entrywiseProduct(this, this, a);
		return this;
	}

	public Vector entrywiseProductCopy(Vector x) {
		Vector a = newInstance(x.length());
		entrywiseProduct(a, this, x);
		return a;
	}

	public Vector times(double d) {
		Vector a = newInstance(this.length());
		entrywiseProduct(a, this, d);
		return a;
	}

	protected static double sumOfEntryProduct(Vector a, Vector b) {
		double sum = 0;
		for (int i = 0; i < a.length(); i++) {
			sum += (a.get(i) * b.get(i));
		}

		return sum;
	}

	// public abstract Vector times(Vector x);

	public void set(double d) {
		for (int i = 0; i < this.length(); i++) {
			this.set(d, i);
		}
	}

	@Override
	public String toString() {
		String str = "";
		for (int i = 0; i < length(); i++) {
			str = str + ", " + get(i);
		}
		return str.substring(2) + "\n";
	}

	public Vector copy() {
		Vector f = newInstance(length());
		for (int i = 0; i < length(); i++) {
			f.set(this.get(i), i);
		}

		return f;
	}

	public static Vector product(Vector... Vector) {
		return product(Arrays.asList(Vector));
	}

	public static Vector sum(Vector... Vector) {
		return sum(Arrays.asList(Vector));
	}

	public static Vector product(List<Vector> Vector) {
		Vector f = Vector.get(0);
		for (int i = 1; i < Vector.size(); i++) {
			f.timesEqual(Vector.get(1));
		}

		return f;
	}

	public static Vector sum(List<Vector> Vector) {
		Vector f = Vector.get(0);
		for (int i = 1; i < Vector.size(); i++) {
			f.add(Vector.get(1));
		}

		return f;
	}

	public void set(Vector a) {
		if (a.length() != length()) {
			new Exception("Not supported at the moment!").printStackTrace();
		}

		for (int i = 0; i < length(); i++) {
			set(a.get(i), i);
		}
	}

	public Vector concat(Vector a) {
		int n = length() + a.length();
		Vector f = newInstance(n);
		for (int i = 0; i < n; i++) {
			if (i < length()) {
				f.set(get(i), i);
			} else {
				f.set(a.get(i - length()), i);
			}
		}

		return f;
	}

	public Vector pow(int n) {
		Vector ret = this.copy();

		for (int i = 1; i < n; i++) {
			ret.timesEqual(this);
		}

		return ret;
	}

	public VectorMatrix divideCopy(Vector a) {
		Vector f = newInstance(length());
		for (int i = 0; i < length(); i++) {
			f.set(get(i) / a.get(i), i);
		}
		return f;
	}

	@Override
	public VectorMatrix minus(VectorMatrix m) {
		return add(m.timesCopy(-1));
	}

	@Override
	public VectorMatrix minusEqual(VectorMatrix m) {
		return minusEqual((Vector) m);
	}

	// @Override
	// public VectorMatrix timesCopy(VectorMatrix m) {
	// return timesCopy(((Vector) m));
	// }
	//
	// @Override
	// public VectorMatrix times(VectorMatrix m) {
	// return times((Vector) m);
	// }

	@Override
	public VectorMatrix add(VectorMatrix m) {
		Vector a = newInstance(m.length());
		// if (m instanceof Matrix) {
		// m = ((Matrix) m).getVector(0);
		// }
		sum(a, this, m);
		return a;
	}

	@Override
	public VectorMatrix addEqual(VectorMatrix m) {
		return add((Vector) m);
	}

	@Override
	public double sumAllElements() {
		double s = 0;

		for (int i = 0; i < length(); i++) {
			s += get(i);
		}

		return s;
	}

	@Override
	public VectorMatrix divideEqual(double d) {
		return times(1. / d);
	}
}
